package config

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"reflect"
	"strconv"
	"sync"

	"gitee.com/ironCoffers/goHelper/config/base"
	"github.com/pelletier/go-toml"
	"gopkg.in/yaml.v2"
)

type defaultConfig struct {
	fileName string
	fileType base.FileType
	data     map[string]interface{}
	rwMutex  sync.RWMutex
}

func (dc *defaultConfig) GetInt(key string) (val int, err error) {
	dc.rwMutex.RLock()
	d, ok := dc.data[key]
	dc.rwMutex.RUnlock()
	if !ok {
		err = fmt.Errorf("[Config GetInt] key [%s] not found", key)
		return
	}
	switch d.(type) {
	case string:
		val, err = strconv.Atoi(d.(string))
	case int:
		val = d.(int)
		err = nil
	default:
		err = fmt.Errorf("[Config GetInt] value of key [%s] isn't Int", key)
	}
	return
}

func (dc *defaultConfig) GetIntDefault(key string, defaultInt int) int {
	if v, err := dc.GetInt(key); err != nil {
		return defaultInt
	} else {
		return v
	}
}

func (dc *defaultConfig) GetString(key string) (val string, err error) {
	dc.rwMutex.RLock()
	d, ok := dc.data[key]
	dc.rwMutex.RUnlock()
	if !ok {
		err = fmt.Errorf("[Config GetString] key [%s] not found", key)
	}
	switch d.(type) {
	case string:
		val = d.(string)
	default:
		err = fmt.Errorf("[Config GetString] value of key [%s] isn't string", key)
	}
	return
}

func (dc *defaultConfig) GetStringDefault(key, defaultString string) string {
	if v, err := dc.GetString(key); err == nil {
		return v
	}
	return defaultString
}

func (dc *defaultConfig) GetMap(key string) (map[string]interface{}, error) {
	dc.rwMutex.RLock()
	d, ok := dc.data[key]
	dc.rwMutex.RUnlock()
	if !ok {
		return nil, fmt.Errorf("[Config GetMap] key [%s] not found", key)
	}
	if reflect.TypeOf(d).Kind() == reflect.Map {
		return d.(map[string]interface{}), nil
	}
	return nil, fmt.Errorf("[Config GetMap] key [%s] of type invaild", key)
}

func (dc *defaultConfig) GetInterface(key string) (val interface{}, err error) {
	dc.rwMutex.RLock()
	val, ok := dc.data[key]
	dc.rwMutex.RUnlock()
	if ok {
		err = fmt.Errorf("[Config GetInterface] key [%s] not found", key)
	}
	return
}

func (dc *defaultConfig) parse() error {
	fileType, data, err := parseFile(dc.fileName)
	if err != nil {
		return err
	}
	dc.rwMutex.Lock()
	dc.fileType = fileType
	dc.data = data
	dc.rwMutex.Unlock()
	return nil
}

func parseFile(filename string) (base.FileType, map[string]interface{}, error) {
	index, err := base.HasSuffix(filename)
	if err != nil {
		return 0, nil, err
	}
	bytes, err := ioutil.ReadFile(filename)
	if err != nil {
		return 0, nil, err
	}
	var data map[string]interface{}
	switch index {
	case base.JSON:
		if err := json.Unmarshal(bytes, &data); err != nil {
			return 0, nil, err
		}
	case base.TOML:
		tree, err := toml.LoadBytes(bytes)
		if err != nil {
			return 0, nil, err
		}
		data = tree.ToMap()
	case base.YAML, base.YAM:
		if err := yaml.Unmarshal(bytes, &data); err != nil {
			return 0, nil, err
		}
	default:
		return 0, nil, errors.New("FileType isn't exsit")
	}
	return index, data, nil
}

//DConfig 返回一个默认的配置对象
func DConfig(fileName string) (base.BaseConfig, error) {
	dc := &defaultConfig{
		fileName: fileName,
	}
	if err := dc.parse(); err != nil {
		return nil, err
	}
	return dc, nil
}
